﻿Public Class PwdGenerator

    ''' <summary>
    ''' Generates Random Password
    ''' </summary>
    ''' <param name="MinimumLength">Minimum length required for the password</param>
    ''' <param name="MaximumLength">Maximum length limited to the password</param>
    ''' <param name="IncSymbols">Include or not some symbols in the password</param>
    ''' <returns>A Random Generated Password is returned</returns>
    Public Shared Function Generate(Optional ByVal MinimumLength As Int32 = 10, Optional ByVal MaximumLength As Int32 = 32, Optional ByVal IncSymbols As Boolean = True) As String
        Static Dim SymbolSet As Char() = New Char() {CChar("!"), CChar("@"), CChar("$"), CChar("*"), CChar("-"), CChar("."), CChar("_")}
        Dim time As DateTime = DateTime.Now
        Dim pwd As String = time.Ticks.ToString()
        Dim listCodes As New List(Of String)
        Dim code As String = String.Empty
        Dim iIndex As Integer = 0
        Dim codeNum As Integer
        Dim optionCount As Integer = 3
        Dim r As New Random(1)
        Dim symbolIndex As Integer = 0
        Static Dim prevSymbolIndex As Integer = 0

        If (MaximumLength = 0) OrElse (MinimumLength = 0) Then
            Throw New ApplicationException("Both Minimum length and Maximum length must be greater than zero.")
        End If

        If MaximumLength < MinimumLength Then
            Throw New ApplicationException("Maximum length must greater than or equal to Minimum length.")
        End If

        pwd = pwd.Replace(" ", "")
        pwd = time.Millisecond.ToString() & time.Second.ToString() & pwd.Substring(pwd.Length \ 3)

        For Each ch As Char In pwd.ToCharArray
            If (code = String.Empty) OrElse (code.Length = 1) Then
                code = code & ch
            Else
                listCodes.Add(code)
                code = ch
            End If
        Next

        If IncSymbols Then
            optionCount = 4
        End If

        pwd = String.Empty
        For iIndex = 0 To listCodes.Count - 1
            codeNum = Convert.ToInt32(listCodes(iIndex))
            codeNum = codeNum Mod 26
            Select Case iIndex Mod optionCount
                Case 0
                    pwd = pwd & Chr(Asc("a") + codeNum)
                Case 1
                    pwd = pwd & Chr(Asc("A") + codeNum)
                Case 2
                    pwd = pwd & codeNum.ToString()
                Case 3
                    codeNum = codeNum Mod SymbolSet.Length
                    symbolIndex = time.Millisecond Mod SymbolSet.Length

                    While (prevSymbolIndex = symbolIndex)
                        symbolIndex = DateTime.Now.Millisecond Mod SymbolSet.Length
                        System.Threading.Thread.Sleep(2)
                    End While
                    prevSymbolIndex = symbolIndex
                    pwd = pwd & SymbolSet(symbolIndex)
            End Select
        Next

        If pwd.Length > MaximumLength Then
            pwd = pwd.Substring(0, MaximumLength)
        End If

        While pwd.Length < MinimumLength
            pwd = pwd & Generate(1, MinimumLength - pwd.Length, IncSymbols)
        End While

        Return pwd
    End Function

End Class
